import numpy
import cython
if not cython.compiled:
    from math import *

DTYPE = numpy.float


def cross(a, b):
    return a.cross(b)

class Vec:
    """
    This class represents a Vector in 3D space.
    """

    # Declared in the .pxd file.
    
    def __init__(self, x=0.0, y=0.0, z=0.0):
        """Constructor for a Vec."""
        self.x = x
        self.y = y
        self.z = z

    def __reduce__(self):
        """
        Implemented to facilitate pickling of the Vec extension type.
        """
        d = {}
        d['x'] = self.x
        d['y'] = self.y
        d['z'] = self.z
        return (Vec, (), d)

    def __setstate__(self, d):
        self.x = d['x']
        self.y = d['y']
        self.z = d['z']

    def __str__(self):
        return '(%f, %f, %f)'%(self.x, self.y, self.z)

    def __repr__(self):
        return 'Vec(%g, %g, %g)'%(self.x, self.y, self.z)

    def __add__(self, p):
        return Vec_add(self,p)

    def __sub__(self, p):
        return Vec_sub(self,p)

    def __mul__(self, m):
        if(isinstance(self, Vec)):
            return self.mul(m)
        return m.mul(self)  
    
    def __div__(self, m):
        return self.mul(1.0/m)

    def __abs__(self):
        return Vec_length(self)

    def __neg__(self):
        return Vec_new(-self.x, -self.y, -self.z)

    def __richcmp__(self, p, oper):
        return self.richcmp(p,oper)

    def __iadd__(self, p):
        return self.iadd(p)

    def __isub__(self, p):
        return self.isub(p)

    def __imul__(self, m):
        return self.imul(m)

    def __idiv__(self, m):
        return self.idiv(m)
        
    def __getitem__(self, i):
        return self.getitem(i)
    
    def __setitem__(self, i, val):
        self.setitem(i,val)
        
    def mul(self, m):
        return Vec_new(self.x*m, self.y*m, self.z*m)
    
    def richcmp(self,p,oper):
        if oper == 2: # ==
            if self.x == p.x and self.y == p.y and self.z == p.z:
                return True
            return False
        elif oper == 3: # !=
            if self.x == p.x and self.y == p.y and self.z == p.z:
                return False 
            return True
        else:
            raise TypeError('No ordering is possible for Vecs.')
    
    def iadd(self,p):
        self.x += p.x
        self.y += p.y
        self.z += p.z
        return self
    
    def isub(self,p):
        self.x -= p.x
        self.y -= p.y
        self.z -= p.z
        return self
    
    def imul(self,m):
        self.x *= m
        self.y *= m
        self.z *= m
        return self
    
    def idiv(self,m):
        self.x /= m
        self.y /= m
        self.z /= m
        return self
    
    def getitem(self,i):
        return (&self.x)[i]
    
    def setitem(self,i,val):
        (&self.x)[i] = val
    
    def add_inplace(self, p):
        self.x += p.x
        self.y += p.y
        self.z += p.z

    def set(self,x,y,z):
        """Set the position from a given array.
        """
        self.x = x
        self.y = y
        self.z = z

    def asarray(self):
        """Return a numpy array with the coordinates."""
        r = numpy.empty(3)
        r[0] = self.x
        r[1] = self.y
        r[2] = self.z
        return r

    def norm(self):
        """Return the square of the Euclidean distance to this Vec."""
        return (self.x*self.x + self.y*self.y + self.z*self.z)

    def length(self):
        """Return the Euclidean distance to this Vec from origin."""
        return sqrt(self.norm())

    def dot(self, p):
        """Return the dot product of this Vec with another."""
        return self.x*p.x + self.y*p.y + self.z*p.z

    def cross(self, p):
        """Return the cross product of this Vec with another, i.e.
        `self` cross `p`."""
        return Vec_new(self.y*p.z - self.z*p.y, 
                       self.z*p.x - self.x*p.z,
                       self.x*p.y - self.y*p.x)

    def distance(self, p):
        """Return the distance between this Vec and p"""
        return sqrt((p.x-self.x)*(p.x-self.x)+
                    (p.y-self.y)*(p.y-self.y)+
                    (p.z-self.z)*(p.z-self.z))

def Vec_new(x=0.0,y=0.0,z=0.0):
    p = Vec.__new__(Vec)
    p.x = x
    p.y = y
    p.z = z
    return p

def Vec_sub(pa,pb):
    return Vec_new(pa.x-pb.x, pa.y-pb.y, pa.z-pb.z)

def Vec_add(pa,pb):
    return Vec_new(pa.x+pb.x, pa.y+pb.y, pa.z+pb.z)


def Vec_length(p):
    return sqrt(p.x*p.x + p.y*p.y + p.z*p.z)

def Vec_length2(p):
    return p.x*p.x + p.y*p.y + p.z*p.z

def Vec_distance(pa,pb):
    return sqrt((pa.x-pb.x)*(pa.x-pb.x) +
                (pa.y-pb.y)*(pa.y-pb.y) + 
                (pa.z-pb.z)*(pa.z-pb.z)
                )

def Vec_distance2(pa,pb):
    return ((pa.x-pb.x)*(pa.x-pb.x) + (pa.y-pb.y)*(pa.y-pb.y) + 
                    (pa.z-pb.z)*(pa.z-pb.z))

class IntVec(Vec):
    """
    A class exactly similar to Vec, just that it is defined for integers instead
    of floats.
    """
    def __init__(self,x=0, y=0,z=0):
        self.x = x
        self.y = y
        self.z = z
